#include "game_spu.h"
#include "game_macros.h"
#include "game_config.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <termios.h>


CGameSerial::CGameSerial():_fd(0),_baudrate(0){

}


BOOL CGameSerial::init(const char* dev, UINT32 baudrate){
  do{

    CC_BREAK_IF_LOG(0 < _fd ,"there should be only init once with dev [%s].",dev);

    CC_BREAK_IF_LOG(0 > (_fd = open(dev, O_RDWR | O_NOCTTY)),"CGameSerial error: %s when open %s",strerror(errno),dev);

    CC_BREAK_IF(!setBaudrate(baudrate));

    return TRUE;
  }while(true);
  return FALSE;
}

BOOL CGameSerial::deinit(){
  if (_fd <= 0) {
    return TRUE;
  }
  close(_fd);
  _fd = 0;
  return TRUE;

}

UINT32 CGameSerial::read(void* buf,size_t bufSize){
  return ::read(_fd, buf, bufSize);
}

UINT32 CGameSerial::write(const void* dat,size_t datSize){
  return ::write(_fd, dat, datSize);
}


BOOL CGameSerial::setBaudrate(UINT32 baudrate){
  do{
    CC_BREAK_IF_LOG(0 > _fd ,"CGameSerial error: %s","not init when setBaudrate");

    UINT32 baudrate_cflag;
    CC_BREAK_IF_LOG(!getBaudrateFlag(baudrate,baudrate_cflag),"CGameSerial error: %s","unsupported baudrate");


    CC_BREAK_IF(!setTTYAttribs(_fd, baudrate_cflag, 0));


    _baudrate = baudrate;
    return TRUE;
  }while(true);
  return FALSE;
}

UINT32 CGameSerial::getBaudrate(){
  return _baudrate;
}

BOOL CGameSerial::setTTYAttribs(int fd, int speed, int parity){
  struct termios tty;
  if (tcgetattr (fd, &tty) != 0)
  {
    C_LOG("CGameSerial error: %d from tcgetattr: %s", errno, strerror(errno));
    return FALSE;
  }

  cfsetospeed (&tty, speed);
  cfsetispeed (&tty, speed);

  tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
  // disable IGNBRK for mismatched speed tests; otherwise receive break
  // as \000 chars
  tty.c_iflag &= ~IGNBRK;         // disable break processing
  tty.c_lflag = 0;                // no signaling chars, no echo,
                                  // no canonical processing
  tty.c_oflag = 0;                // no remapping, no delays
  tty.c_cc[VMIN]  = 0;            // read doesn't block
  tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

  tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                  // enable reading
  tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
  tty.c_cflag |= parity;
  tty.c_cflag &= ~CSTOPB;
  tty.c_cflag &= ~CRTSCTS;

  tty.c_iflag &= ~ICRNL; //禁止将输入的CR转换为NL
  tty.c_iflag &= ~(IXON); //清bit位 关闭流控字符

  if (tcsetattr (fd, TCSANOW, &tty) != 0) {
    C_LOG("CGameSerial error: %d from tcsetattr: %s", errno, strerror(errno));
    return FALSE;
  }
  return TRUE;

}


BOOL CGameSerial::getBaudrateFlag(UINT32 baudrate, UINT32 &cflags){
  const BaudTableItem_t baudTable[] = {
    { 50,         B50	      },
    { 75,         B75       },
    { 150,        B150      },
    { 200,        B200      },
    { 300,        B300      },
    { 600,        B600      },
    { 1200,       B1200     },
    { 1800,       B1800     },
    { 2400,       B2400     },
    { 4800,       B4800     },
    { 9600,       B9600     },
    { 19200,      B19200    },
    { 38400,      B38400    },
    { 57600,      B57600    },
    { 115200,     B115200    },
    { 230400,     B230400    },
#if CGAME_TARGET_PLATFORM != CGAME_PLATFORM_MAC
    { 460800,     B460800    },
    { 500000,     B500000    },
    { 576000,     B576000    },
    { 921600,     B921600    },
    { 1000000,    B1000000    },
    { 1152000,    B1152000    },
    { 1500000,    B1500000    },
    { 2000000,    B2000000    },
    { 2500000,    B2500000    },
    { 3000000,    B3000000    },
    { 3500000,    B3500000    },
    { 4000000,    B4000000    },
#endif
  };

  for (auto i : baudTable) {
    if ((UINT32)i.baud == baudrate) {
      cflags = i.flag;
      return TRUE;
    }
  }
  return FALSE;


}
